package com.yt.sys.server.common.exception;

import com.alibaba.fastjson.JSON;
import com.yt.common.constant.CommonParameter;
import com.yt.common.constant.Result;
import com.yt.common.enums.CodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.stream.Collectors;

/**
 * @Description ：创建全局异常处理类：通过使用@ControllerAdvice定义统一的异常处理类，而不是在每个Controller中逐个定义。@ExceptionHandler用来定义函数针对的异常类型，最后将Exception对象和请求URL映射到error.html中
 * @Author ： 解风
 * @Date ： 2017/09/26 14:22
 */
@Slf4j
@ControllerAdvice
public class SysGlobalExceptionHandler {

    public static final String DEFAULT_ERROR_VIEW = "error";

    /**
     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
     * @param binder
     */
    @InitBinder
    public void initBinder(WebDataBinder binder) {}

    /**
     * 把值绑定到Model中，使全局@RequestMapping可以获取到该值
     * @param model
     */
    @ModelAttribute
    public void addAttributes(Model model) {
        model.addAttribute("author", "Magical Sam");
    }

    /**
     * 处理参数校验异常
     * @param req
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    @ResponseBody
    public Result handleMethodArgumentNotValidException(HttpServletRequest req, MethodArgumentNotValidException e) throws Exception {
        String errorMessage = e.getBindingResult().getFieldErrors().stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining(", "));
        
        log.error("参数校验异常====>"+ JSON.toJSONString(CommonParameter.getHeadParamsBO().getBodyParams()), e);
        return Result.fail(errorMessage);
    }

    /**
     * 处理参数校验异常（针对GET请求参数）
     * @param req
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = BindException.class)
    @ResponseBody
    public Result handleBindException(HttpServletRequest req, BindException e) throws Exception {
        String errorMessage = e.getBindingResult().getFieldErrors().stream()
                .map(error -> error.getField() + ": " + error.getDefaultMessage())
                .collect(Collectors.joining(", "));
        
        log.error("参数绑定异常====>"+ JSON.toJSONString(CommonParameter.getHeadParamsBO().getBodyParams()), e);
        return Result.fail(errorMessage);
    }

    /**
     * 处理参数校验异常（针对PathVariable和RequestParam）
     * @param req
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = ConstraintViolationException.class)
    @ResponseBody
    public Result handleConstraintViolationException(HttpServletRequest req, ConstraintViolationException e) throws Exception {
        String errorMessage = e.getConstraintViolations().stream()
                .map(ConstraintViolation::getMessage)
                .collect(Collectors.joining(", "));
        
        log.error("参数约束异常====>"+ JSON.toJSONString(CommonParameter.getHeadParamsBO().getBodyParams()), e);
        return Result.fail(errorMessage);
    }

    /**
     * 处理数据完整性约束异常（如字段非空约束）
     * @param req
     * @param e
     * @return
     * @throws Exception
     */
    @ExceptionHandler(value = {DataIntegrityViolationException.class, SQLIntegrityConstraintViolationException.class})
    @ResponseBody
    public Result handleDataIntegrityViolationException(HttpServletRequest req, Exception e) throws Exception {
        log.error("数据完整性约束异常====>"+ JSON.toJSONString(CommonParameter.getHeadParamsBO().getBodyParams()), e);
        // 检查异常信息中是否包含NOT NULL约束相关的内容
        if (e.getMessage().contains("NOT NULL") || e.getMessage().contains("null")) {
            return Result.fail("数据不能为空，请检查必填字段是否完整");
        }
        return Result.fail("数据完整性约束异常，请检查数据是否符合要求");
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result jsonErrorHandler(HttpServletRequest req, Exception e) throws Exception {
        Result rs = new Result();
        rs.setCode(CodeEnum.FAIL_100005.getCode());
        rs.setMsg(CodeEnum.FAIL_100005.getMsg());
        e.printStackTrace();
        log.error("系统异常参数====>"+ JSON.toJSONString(CommonParameter.getHeadParamsBO().getBodyParams()), e);
        return rs;
    }
}